home *** CD-ROM | disk | FTP | other *** search
/ Celestin Apprentice 4 / Apprentice-Release4.iso / Source Code / C / Code Resources / Jims CDEFs 1.50 / CDEF Source / source / cdefPopMenu.c < prev    next >
Encoding:
Text File  |  1995-11-08  |  50.1 KB  |  1,470 lines  |  [TEXT/KAHL]

  1. //------------------------- © 1991-1995 by James G. Stout --------------------------
  2. // File        : cdefPopMenu.c
  3. // Date        : September 1,1991
  4. // Author    : Jim Stout
  5. // Purpose    : popup menu cdef
  6. //             : Implements most of the features of the Apple popUpMenu CDEF 63, but
  7. //            : works the same in System 6 & 7 and supports 'mctb' resources.
  8. //            :
  9. //            : This should be "plug compatible" with the Apple CDEF but also has
  10. //            : 7 handy additional variations.
  11. //            :
  12. //            : see cdefPopMenu.h for more detail and variation codes
  13. //            :
  14. //            : If you find a use for this, I'd love to know about it.  Bug reports
  15. //            : are always interesting.
  16. //            :
  17. //            : Internet    : JimS@WRQ.COM(work hours, PST)
  18. //            : AppleLink   : WRQ            (daily)
  19. //            : CompuServe  : 73240,2052    (weekly or so)
  20. //            : AOL         : JasG        (weekly or so)
  21. //            : eWorld      : Jim Stout    (weekly or so)
  22. //----------------------------------------------------------------------------------
  23. //#define _DEBUGCDEF 1            // uncomment to do source debugging
  24.  
  25. // uncomment the following to have the popupInsetFrame variation use the menu's
  26. // background color instead of the grafPort's background color
  27.  
  28. //#define USE_PORT_BG
  29.  
  30. #include "fatCDEF.h"
  31.  
  32. #include <Controls.h>
  33. #include <GestaltEqu.h>
  34. #include <Icons.h>
  35. #include <LowMem.h>
  36. #include <Memory.h>
  37. #include <Palettes.h>
  38. #ifdef __powerc
  39. #include <MixedMode.h>
  40. #endif
  41. #include <Resources.h>
  42. #include <ToolUtils.h>
  43. #include <Traps.h>
  44. #include <Types.h>
  45.  
  46. #include "colorCDEF.h"
  47. #include "miscCDEF.h"
  48. #include "cdefPopMenu.h"
  49.  
  50. //----------------------------------------------------------------------------------
  51. // CDEF globals
  52. //----------------------------------------------------------------------------------
  53.  
  54. short                gMenuW;
  55. UniversalProcPtr    CalcMenuSizeUPP;
  56.  
  57. //----------------------------------------------------------------------------------
  58. // need to patch the CalcMenuSize trap
  59. //----------------------------------------------------------------------------------
  60. typedef pascal void ( *CMSFuncPtr ) ( MenuHandle theMenu );
  61. #define CallCalcMenuSize(theMenu)    (*(CMSFuncPtr)CalcMenuSizeUPP)( theMenu );
  62.  
  63. //----------------------------------------------------------------------------------
  64. #if _DEBUGCDEF
  65. pascal long CDmain (short varCode, ControlHandle theCtl, short message, long param);
  66.  
  67. pascal long CDmain (short varCode, ControlHandle theCtl, short message, long param)
  68. #else
  69.  
  70. //==================================================================================
  71. // Main entry point for the CDEF
  72. //==================================================================================
  73.  
  74. pascal long main (short varCode, ControlHandle theCtl, short message, long param)
  75. #endif
  76. {
  77.     long                ret = 0;
  78.     Point                mPt;
  79.     popUpPrivateDataH    pd;
  80.     SignedByte            cState, dState;
  81.     
  82. #include "fatEntry.c"
  83.     
  84.     cState = HGetState((Handle)theCtl);
  85.     HLock((Handle)theCtl);
  86.     if((**theCtl).contrlData) {
  87.         dState = HGetState((**theCtl).contrlData);
  88.         HLock((**theCtl).contrlData);
  89.         pd = (popUpPrivateDataH)(*theCtl)->contrlData;
  90.     }
  91.  
  92.     switch(message) {
  93.         case initCntl:
  94.             doInit(theCtl, varCode);    
  95.             if((**theCtl).contrlData) {
  96.                 dState = HGetState((**theCtl).contrlData);
  97.                 HLock((**theCtl).contrlData);
  98.             }
  99.         break;
  100.         case dispCntl:
  101.             doDisp(theCtl, varCode);    
  102.         break;
  103.         case drawCntl:
  104.             if((**theCtl).contrlVis != 0 && 
  105.                 ((WindowPeek)(**theCtl).contrlOwner)->visible)
  106.                 doDraw(theCtl,varCode);        
  107.         break;
  108.         case autoTrack:
  109.             doPopUp(theCtl,varCode);    
  110.         break;
  111.         case testCntl:
  112.             mPt.v=HiWord(param);
  113.             mPt.h=LoWord(param);
  114.             if(PtInRect(mPt,&(*pd)->rItem) || PtInRect(mPt,&(*pd)->rTitle))
  115.                 ret = 1L;
  116.         break;
  117.         case calcCRgns:
  118.             RectRgn((RgnHandle)(param & 0x7fffffffL), &(*theCtl)->contrlRect);
  119.         break;
  120.         case calcCntlRgn:
  121.         case calcThumbRgn:
  122.             RectRgn((RgnHandle)(param), &(*theCtl)->contrlRect);
  123.         break;
  124.     }
  125.  
  126.     if((**theCtl).contrlData)
  127.         HSetState((**theCtl).contrlData, dState);
  128.     HSetState((Handle)theCtl, cState);
  129.  
  130. #include "fatExit.c"
  131.  
  132.     return(ret);
  133. }
  134.  
  135. //==================================================================================
  136. //    Initialize our private data
  137. //==================================================================================
  138.  
  139. static void doInit(ControlHandle theCtl, short varCode)
  140. {
  141.     MenuHandle            hMenu;
  142.     popUpPrivateDataH    pd=0;    
  143.     Boolean                popAvail;
  144.     GrafPtr                thisPort;
  145.     
  146.     GetPort(&thisPort);
  147.  
  148. //----------------------------------------------------------------------------------
  149. // create and initialize our private data    
  150. //----------------------------------------------------------------------------------
  151.     
  152.     pd = (popUpPrivateDataH)NewHandle(sizeof(popUpPrivateData));
  153.     if(pd) {
  154.         HLock((Handle)pd);
  155.         (*pd)->mHandle = hMenu = 0;
  156.         popAvail = trapAvailable(_PopUpMenuSelect);
  157.         
  158.         if(popAvail) {
  159.         
  160.             (*pd)->hasColorQD = hasColorQD();
  161.             
  162. //----------------------------------------------------------------------------------
  163. // check to see if this control needs to load and release the menu    
  164. //----------------------------------------------------------------------------------
  165.  
  166.             (*pd)->relMenuRes = false;                    // assume no…
  167.             
  168.             if(varCode & popupUseAddResMenu) {            // menu items from resources
  169.                 hMenu = GetMenu((*theCtl)->contrlMin);    // try to load an existing menu    
  170.                 if(hMenu == 0L)                            // nope, create menu for control    
  171.                     hMenu = NewMenu((*theCtl)->contrlMin,"\p");
  172.                 else
  173.                     (*pd)->relMenuRes = true;
  174.                                                              
  175.                 if(*hMenu != 0L)
  176.                     AddResMenu(hMenu,(*theCtl)->contrlRfCon);
  177.             }
  178.             else {
  179.                 SetResLoad(false);
  180.                 hMenu = (MenuHandle)GetResource('MENU',(*theCtl)->contrlMin);
  181.                 SetResLoad(true);
  182.                 if(hMenu == 0L)                            // it's a dynamic menu
  183.                     hMenu = GetMHandle((*theCtl)->contrlMin);    
  184.                 else
  185.                 if(*hMenu == 0L) {                        // empty handle means the    
  186.                     ReleaseResource((Handle)hMenu);        // menu has not been loaded,  
  187.                     hMenu = GetMenu((*theCtl)->contrlMin);// so load the menu…
  188.                     if(hMenu != 0L)
  189.                         (*pd)->relMenuRes = true;        // and release it too
  190.                 }
  191.             }
  192.         
  193. //----------------------------------------------------------------------------------
  194. // initialize our stuff    
  195. //----------------------------------------------------------------------------------
  196.             (*pd)->osVers = getOSVers();                // for later reference
  197.             (*pd)->mHandle = hMenu;                        // handle to menu to pop
  198.             (*pd)->hMenuState = HGetState((Handle)((*pd)->mHandle));
  199.             HNoPurge((Handle)((*pd)->mHandle));            // menus should NEVER be purgeable!
  200.             SetRect(&(*pd)->rItem, 0, 0, 0, 0);    
  201. //----------------------------------------------------------------------------------
  202. // get the funky template values for later reference
  203. //----------------------------------------------------------------------------------
  204.  
  205.             (*pd)->mId = (*theCtl)->contrlMin;                // menuId                    
  206.             (*pd)->tJust = (*theCtl)->contrlValue;            // title justification        
  207.             (*pd)->tWid = (*theCtl)->contrlMax;            
  208.             (*pd)->tWid &= 0xFF;                            // low byte is width for title
  209.             (*pd)->varCode2 = (*theCtl)->contrlMax & 0xFF00;// high byte is extra varCode
  210.             
  211.             (**pd).txFont = thisPort->txFont;
  212.             (**pd).txSize = thisPort->txSize;
  213.                 
  214.             (*theCtl)->contrlData = (Handle)pd;                // save our private data
  215.             
  216. //----------------------------------------------------------------------------------
  217. // then set control template values to some harmless defaults
  218. //----------------------------------------------------------------------------------
  219.     
  220.             (*theCtl)->contrlMin = 1;    
  221.             (*theCtl)->contrlMax = CountMItems(hMenu);
  222.             (*theCtl)->contrlValue = 1;
  223.             (**theCtl).contrlAction = (ControlActionUPP) -1;    // use trackControl    
  224.         }
  225.         HUnlock((Handle)pd);
  226.     }
  227. }
  228.  
  229. //==================================================================================
  230. //    Clean up and dispose of our private data
  231. //==================================================================================
  232.  
  233. static void doDisp(ControlHandle theCtl, short varCode)
  234. {    
  235.     popUpPrivateDataH    pd;
  236.         
  237.     pd = (popUpPrivateDataH)(*theCtl)->contrlData;
  238.     if(pd) {
  239.         if ((*pd)->mHandle && *(*pd)->mHandle) {
  240.             HSetState((Handle)(*pd)->mHandle, (*pd)->hMenuState);
  241.         
  242.             if((*pd)->varCode2 & popupNoDeleteMenu) {
  243.                 DeleteMenu((*(*pd)->mHandle)->menuID);
  244.             }
  245.             if(varCode & popupUseAddResMenu) {
  246.                 if((*pd)->mHandle != NULL && (*pd)->relMenuRes)    {
  247.                     ReleaseResource((Handle)(*pd)->mHandle);
  248.                 }
  249.                 else {
  250.                     DisposeMenu((*pd)->mHandle);
  251.                 }
  252.             }
  253.             else {    
  254.                 if((*pd)->mHandle != NULL && (*pd)->relMenuRes)    {
  255.                     ReleaseResource((Handle)(*pd)->mHandle);
  256.                 }
  257.             }
  258.         }
  259.         HUnlock((Handle)pd);
  260.         DisposHandle((Handle)pd);
  261.         (*theCtl)->contrlData = 0;
  262.     }
  263. }
  264.  
  265. //==================================================================================
  266. // If running with System 7, use DeviceLoop to gracefully handle multiple screens.
  267. // Simulate DeviceLoop if using System 6...
  268. //==================================================================================
  269.  
  270. static void doDraw(ControlHandle theCtl, short varCode)
  271. {
  272.     devLoopHandle            hDl;
  273.     RgnHandle                saveClip, hRgn;
  274.     DeviceLoopDrawingUPP    drawControlUPP;
  275.  
  276. //----------------------------------------------------------------------------------
  277. //    Create our data handle to pass to DeviceLoop
  278. //----------------------------------------------------------------------------------
  279.  
  280.     hDl = (devLoopHandle)NewHandle(sizeof(devLoopData));
  281.     if(hDl) {
  282.         drawControlUPP = NewDeviceLoopDrawingProc(drawControl);
  283.         
  284.         (**hDl).theCtl = theCtl;
  285.         (**hDl).varCode = varCode;
  286.         (**hDl).controlRect = (**theCtl).contrlRect;
  287.  
  288. //----------------------------------------------------------------------------------
  289. //    Do the clip region properly.  Thanks Ari!
  290. //----------------------------------------------------------------------------------
  291.  
  292.         saveClip = NewRgn();
  293.         GetClip(saveClip);
  294.         
  295.         hRgn = NewRgn();
  296.         RectRgn(hRgn, &(**hDl).controlRect);
  297.         SectRgn(saveClip, hRgn, hRgn);
  298.         
  299.         if(EmptyRgn(hRgn)) {                                    // if empty, don't waste
  300.             DisposeRgn(saveClip);                                // time drawing...
  301.             DisposeRgn(hRgn);
  302.             return;
  303.         }        
  304.  
  305. //----------------------------------------------------------------------------------
  306. //    Call DeviceLoop to take care of our drawing
  307. //----------------------------------------------------------------------------------
  308.         
  309.         if(getOSVers() >= 0x0700) {
  310.             DeviceLoop (hRgn, drawControlUPP, (long)hDl, 0);
  311.         }
  312.         else {
  313.             sys6DeviceLoop (hRgn, drawControlUPP, (long)hDl, 0);
  314.         }
  315.  
  316.         SetClip(saveClip);        
  317.         DisposeRgn(hRgn);
  318.         DisposeHandle((Handle)hDl);
  319.         DisposeRoutineDescriptor(drawControlUPP);
  320.     }
  321. }
  322.  
  323. //==================================================================================
  324. //    Draw the control - using the proper menu colors.  The aim here is to draw an 
  325. //    exact representation of what the Menu Manager draws for a menu item, but inside
  326. //    a drop-shadowed rectangle.  If we don't match what the Menu Mgr draws, the
  327. //  menu item text will shift when the menu is popped.
  328. //
  329. // This is called by DeviceLoop, but in this case, we just pay attention to the
  330. // "depth" and "userData" parameters.
  331. //==================================================================================
  332.  
  333. pascal void drawControl (short depth, short dFlags, GDHandle theDevice, long userData)
  334. {
  335.  
  336. #pragma unused(dFlags, theDevice)
  337.  
  338.     ControlHandle         hCtl;
  339.     short                 varCode,saveFont,saveSize,saveFace;
  340.     short                cntlW,w,len,iconNum,cmdChar;
  341.     long                inx;
  342.     Str255                mStr;
  343.     Boolean                sysFont,inactive=false,itemDisable=false;
  344.     Boolean                inColor=false,bgInColor=false,gotGray=false;
  345.     PenState            savePen;
  346.     Rect                tempRect;
  347.     Style                itemStyle;
  348.     RGBColor            saveFore,saveBack,menuFore,menuBack,tempFore;
  349.     RGBColor            rgbWhite={-1,-1,-1};
  350.     RGBColor            rgbA = {0xAAAA,0xAAAA,0xAAAA};
  351.     GDHandle            hThisDevice;
  352.     GrafPtr                thisPort;
  353.     popUpPrivateDataH    pd;
  354.     devLoopHandle        hDl;
  355.     
  356. //----------------------------------------------------------------------------------
  357. // Can we draw?
  358. //----------------------------------------------------------------------------------
  359.  
  360.     hDl = (devLoopHandle)userData;                        // need control & varCode
  361.     if(hDl) {
  362.         hCtl = (**hDl).theCtl;
  363.         varCode = (**hDl).varCode;
  364.     }
  365.     else
  366.         return;
  367.         
  368.     pd = (popUpPrivateDataH)(*hCtl)->contrlData;        // get our private data
  369.     if(!pd)                                                // oops !                    
  370.         return;
  371.     if(!(*pd)->mHandle)                                    // oops again !                
  372.         return;
  373.     
  374. //----------------------------------------------------------------------------------
  375. // initialize for drawing - set a few flags & color stuff
  376. //----------------------------------------------------------------------------------
  377.  
  378.     GetPort(&thisPort);
  379.     if(depth > 1 && !(((CGrafPtr)thisPort)->portVersion & 0x8000))
  380.         depth = 1;
  381.         
  382.     if((*hCtl)->contrlValue < 32) {                        // is the item disabled?
  383.         inx = 31 - (*hCtl)->contrlValue;
  384.         if(!BitTst(&(**(*pd)->mHandle).enableFlags, inx))
  385.             itemDisable = true;
  386.     }
  387.     if((*hCtl)->contrlHilite == 255)                    // is the control disabled?
  388.         inactive = itemDisable = true;
  389.         
  390.     if(depth > 2) {
  391.         inColor = true;
  392.         bgInColor = true;
  393.         saveColors(&saveFore,&saveBack);            // save current colors    
  394.         if(saveBack.red == 65535 &&                    // is bg white?
  395.             saveBack.green == 65535 &&
  396.             saveBack.blue == 65535)
  397.             bgInColor = false;
  398.     }
  399.  
  400. //----------------------------------------------------------------------------------
  401. // do font stuff
  402. //----------------------------------------------------------------------------------
  403.  
  404.     if((thisPort->txFont != (**pd).txFont) ||        // if font changed,
  405.         (thisPort->txSize != (**pd).txSize)) {        // clear rect to erase
  406.         (**pd).txFont = thisPort->txFont;            // old font
  407.         (**pd).txSize = thisPort->txSize;
  408.         EraseRect(&(*hCtl)->contrlRect);
  409.     }
  410.     
  411.     sysFont = setFont(thisPort, &saveFont, &saveSize, varCode, (**pd).hasColorQD);
  412.         
  413.     saveFace = thisPort->txFace;
  414.     GetPenState(&savePen);
  415.     PenSize(1,1);
  416.     PenPat( (ConstPatternParam) "\xff\xff\xff\xff\xff\xff\xff\xff");
  417.  
  418. //----------------------------------------------------------------------------------
  419. //    This is gonna destroy any background PICT, but is necessary because we may have
  420. //    different sized ICONs in the menu.
  421. //----------------------------------------------------------------------------------
  422.  
  423.     if((*pd)->varCode2 & popupIconOnly) {
  424.         EraseRect(&(*hCtl)->contrlRect);
  425.     }
  426.     
  427. //----------------------------------------------------------------------------------
  428. // Get locations and sizes for our drawing - these change based on menu item style    
  429. //----------------------------------------------------------------------------------
  430.  
  431.     tempRect = (*pd)->rItem;                                // need this below
  432.     
  433.     getRects(hCtl, sysFont);
  434.     
  435. //----------------------------------------------------------------------------------
  436. //    NOTE: gMenuW is a global that contains a 'calculated' menu width - including
  437. //    the space for the 'popup symbol'.  Later, we will use this to fool the Menu
  438. //    Manager into thinking the menu is wider that it really is.  See 'myCalcMenuSize'
  439. //    routine below and the patch to CalcMenuSize.
  440. // 
  441. //    This lets the 'popped' menu be the same size as our drawing of the current 
  442. //    item + the symbol in rectangle 'rItem'.
  443. //
  444. //  Rect rItem is adjusted by resizePopup() and will be the correct size for the
  445. //    doPop routine.  It is then reset when we enter doDraw after popping the menu.
  446. //
  447. //    Thanks to Chris Faigle for this technique.
  448. //----------------------------------------------------------------------------------
  449.  
  450.     CalcMenuSize((*pd)->mHandle);
  451.     gMenuW = (*(*pd)->mHandle)->menuWidth;                    // normal menu size
  452.     
  453.     if((*pd)->varCode2 & popupIconOnly) {
  454.         if((*pd)->varCode2 & popupInsetFrame) {
  455.             OffsetRect(&(*pd)->rIcon, 1, 0);
  456.             (*pd)->rItem.left+=1;
  457.             (*pd)->rTitle.right = (*pd)->rItem.left;
  458.         }
  459.         (*pd)->rItem.right = (*pd)->rIcon.right + SHADOW;    // set rItem to icon width
  460.     }
  461.     else
  462.     if((*pd)->symWid == 0) {                                // variant without symbol
  463.         cntlW  = (*pd)->rItem.right-(*pd)->rItem.left;        // so set rItem to size of
  464.         if(gMenuW + SHADOW < cntlW)                            // menu if needed.
  465.             (*pd)->rItem.right = (*pd)->rItem.left + gMenuW + SHADOW;
  466.     }
  467.     else                                    
  468.         resizePopup(hCtl, varCode);                            // adjust gMenuW & rItem
  469.     
  470. //----------------------------------------------------------------------------------
  471. //    Now tempRect contains the rect for the previous menu item - adjusted to the 
  472. //    proper width.  Use to erase the control rect.  Doing it this way is really only
  473. //    important if the menu is using styled text.
  474. //----------------------------------------------------------------------------------
  475.  
  476.     tempRect.right = (*pd)->rItem.right;
  477.     EraseRect(&tempRect);
  478.     
  479. //----------------------------------------------------------------------------------
  480. // set colors for drawing
  481. //----------------------------------------------------------------------------------
  482.     
  483.     if (inColor) {            
  484.         hThisDevice = GetGDevice();                
  485.         if(inactive && (*pd)->osVers >= 0x0700) {        // inactive and System 7
  486.             GetForeColor(&menuFore);                    // gray out the popup        
  487.             GetBackColor(&menuBack);                    // the Sys 7 way    
  488.             gotGray = GetGray(hThisDevice,&menuBack,&menuFore);
  489.             if(gotGray)
  490.                 RGBForeColor(&menuFore);                // gray for title & frame    
  491.         }
  492.     }
  493.     
  494. //----------------------------------------------------------------------------------
  495. // draw title, if requested    
  496. //----------------------------------------------------------------------------------
  497.     
  498.     if((*pd)->tWid > 0)    {                                // 0 means no title
  499.         itemStyle = (*pd)->tJust >> 8;
  500.         TextFace(itemStyle);
  501. #ifdef _REALOBSCUREBUG                    
  502. //
  503. // if itemStyle is outline, then the control title should be 1 pixel lower
  504. // than for other styles. I don't know why... If you need this, #define it...
  505. //
  506.         GetItemStyle((*pd)->mHandle, (*hCtl)->contrlValue, &itemStyle);
  507.         if(itemStyle & outline)
  508.             (*pd)->vDraw+=1;
  509. #endif                
  510.         if(inColor && depth > 2) {
  511.             GetForeColor(&tempFore);                    // save this for later    
  512.             if(bgInColor && (varCode & ctl3D)) {        // draw embossed title
  513.                 RGBForeColor(&rgbWhite);
  514.                 MoveTo((*pd)->hTitle+1,(*pd)->vDraw+1);
  515.                 DrawString((*hCtl)->contrlTitle);
  516.                 
  517.             }    
  518.             setPartColor(hCtl, cTextColor, true);        // set text color
  519.         }    
  520.         if(gotGray)                                        // inactive and System 7
  521.             TextMode(grayishTextOr);                    // so draw in gray
  522.             
  523.         MoveTo((*pd)->hTitle,(*pd)->vDraw);                // draw title string here    
  524.         DrawString((*hCtl)->contrlTitle);                // draw it
  525.         
  526.         if(inColor)
  527.             RGBForeColor(&tempFore);                    // back to port foreColor
  528.         TextMode(srcOr);
  529. #ifdef _REALOBSCUREBUG
  530. //
  531. // have to readjust, since the menu item draws as calcuated
  532. //
  533.         if(itemStyle & outline)
  534.             (*pd)->vDraw-=1;
  535. #endif                
  536.     }
  537.     
  538. //----------------------------------------------------------------------------------
  539. // now, do the drawing of the 'popup' frame
  540. //----------------------------------------------------------------------------------
  541.     
  542.     tempRect = (*pd)->rItem;    
  543.     if((*pd)->varCode2 & popupIconOnly) {                // shrink frame a bit so
  544.         tempRect.top = (*pd)->rIcon.top-2;                // it just fits icon
  545.         tempRect.bottom = (*pd)->rIcon.bottom+2;
  546.     }
  547.     else
  548.         tempRect.bottom-=1;                                // inset past the
  549.     tempRect.right-=1;                                    // drop shadow
  550.     
  551.     if(((*pd)->varCode2 & popupInsetFrame) ) {            // Draw an inset popup
  552.         if(inColor) {
  553.             FrameRect(&tempRect);                        // popUp Menu frame    
  554.             RGBForeColor(&rgbA);
  555.             MoveTo(tempRect.left-1,tempRect.bottom);
  556.             LineTo(tempRect.left-1,tempRect.top-1);
  557.             LineTo(tempRect.right-1,tempRect.top-1);
  558.             ForeColor(whiteColor);
  559.             Move(1,0);
  560.             LineTo(tempRect.right,tempRect.bottom);
  561.             LineTo(tempRect.left,tempRect.bottom);
  562.         }
  563.         else 
  564.             drawPopupFrame(&tempRect);
  565.     }
  566.     else                                                // Draw the normal 
  567.         drawPopupFrame(&tempRect);                        // popUp Menu frame    
  568.         
  569.     InsetRect(&tempRect,1,1);                            // the interior of popup
  570.  
  571.         
  572. //----------------------------------------------------------------------------------
  573. // now, draw item string.  Get menu colors & check to see if we need to draw in gray
  574. //----------------------------------------------------------------------------------
  575.     
  576.     if (inColor) {            
  577.         getMenuColors((*pd)->mId,(*hCtl)->contrlValue,    // get the menu colors
  578.                         &menuFore,&menuBack);                            
  579.         RGBForeColor(&menuFore);
  580. #ifdef USE_PORT_BG        
  581.         if(!((*pd)->varCode2 & popupInsetFrame))        // don't use menu bg color
  582. #endif
  583.             RGBBackColor(&menuBack);                    // if inset frame
  584.         
  585.         GetForeColor(&tempFore);
  586.         if((*pd)->osVers >= 0x0700 &&                     // GetGray can be called
  587.             (inactive || itemDisable)) {                // to get "inactive" gray
  588.                                                         // used for System 7
  589.             
  590.             gotGray = GetGray(hThisDevice,&menuBack,&tempFore);
  591.             if(gotGray)
  592.                 RGBForeColor(&tempFore);                // set gray for string    
  593.         }    
  594.     }
  595.     PenPat( (ConstPatternParam) "\x00\x00\x00\x00\x00\x00\x00\x00");
  596.     PaintRect(&tempRect);                                // erase to bg color 
  597.     PenPat( (ConstPatternParam) "\xff\xff\xff\xff\xff\xff\xff\xff");
  598.  
  599. //----------------------------------------------------------------------------------
  600. //  Truncate the item string if needed. Must fit available control width,
  601. //    which includes some leading space and the symbol space on the right.    
  602. //----------------------------------------------------------------------------------        
  603.     
  604.     if(!((*pd)->varCode2 & popupSymbolOnly)) {
  605.         GetItemStyle((*pd)->mHandle, (*hCtl)->contrlValue, &itemStyle);
  606.         TextFace(itemStyle);
  607.         GetItem((MenuHandle)(*pd)->mHandle,                // get menu item string        
  608.                 (*hCtl)->contrlValue,mStr);    
  609.  
  610.         w = StringWidth(mStr);                            // get its width
  611.         
  612.         if((*pd)->varCode2 & popupCenterText) {            // center text in control
  613.             cntlW = tempRect.right-tempRect.left;
  614.             (*pd)->hItem = tempRect.left + (cntlW - w)/2 +1;
  615.         }
  616.         else {                                            // draw in normal, menu
  617.             cntlW = tempRect.right -                    // position but truncate
  618.                 (*pd)->hItem - (*pd)->symWid;            // string if too wide.
  619.                 
  620.             if(cntlW > 0 && mStr[0] > 1) {                // truncate the string
  621.                 while(w > cntlW) {                
  622.                     len = mStr[0]-1;
  623.                     if(len < 1)                            // no endless loops needed    
  624.                         break;
  625.                     mStr[0] = len;
  626.                     if(len > 1)
  627.                         mStr[len] = ELLIPSIS;
  628.                     w = StringWidth(mStr); 
  629.                 };    
  630.             }
  631.         }
  632. //----------------------------------------------------------------------------------
  633. // now go ahead and draw the current menu item string, in 3D if needed.
  634. //----------------------------------------------------------------------------------
  635.         if(bgInColor && (varCode & ctl3D) && ((*pd)->varCode2 & popupInsetFrame)) {
  636.             MoveTo((*pd)->hItem+1, (*pd)->vDraw+1);
  637.             GetForeColor(&tempFore);
  638.             RGBForeColor(&rgbWhite);
  639.             DrawString(mStr);
  640.             RGBForeColor(&tempFore);
  641.         }
  642.         MoveTo((*pd)->hItem, (*pd)->vDraw);
  643.         DrawString(mStr);
  644.     }
  645.         
  646. //----------------------------------------------------------------------------------
  647. // draw the popUp symbol
  648. //---------------------------------------------------------------------------------
  649.         
  650.     if((*pd)->symWid > 0) {
  651.         if(inColor) {
  652.             if((*pd)->varCode2 & popupBlackSymbol) {
  653.                 ForeColor(blackColor);
  654.                 if(inactive && gotGray)    {                // did this before
  655.                     GetForeColor(&tempFore);
  656.                     if(GetGray(hThisDevice,&menuBack,&tempFore))
  657.                         RGBForeColor(&tempFore);        // set gray for symbol    
  658.                 }
  659.             }
  660.             else
  661.             if(!inactive)
  662.                 RGBForeColor(&menuFore);
  663.         }
  664.         drawSymbol(tempRect,(*pd)->symHt, (*pd)->symWid);
  665.     }
  666. //----------------------------------------------------------------------------------
  667. // draw icon
  668. //----------------------------------------------------------------------------------
  669.  
  670.     GetItemIcon((MenuHandle)(*pd)->mHandle,                // is there an icon    ?    
  671.                 (*hCtl)->contrlValue, &iconNum);
  672.     if(iconNum) {
  673.         GetItemCmd((MenuHandle)(*pd)->mHandle,            // what kind?
  674.                     (*hCtl)->contrlValue, &cmdChar);
  675.         plotAnIcon(iconNum, &(*pd)->rIcon, (*pd)->osVers, 
  676.                     cmdChar, itemDisable, (*pd)->hasColorQD, inColor);
  677.     }
  678. //----------------------------------------------------------------------------------
  679. // draw the inactive version - this gets a little complicated when in color and 
  680. // with System 7 - we may have sucessfully drawn the title in gray, but not the
  681. // menu text - if the menu bg was colored but the dialog wasn't...
  682. //----------------------------------------------------------------------------------
  683.  
  684.     if((inactive || itemDisable) && !gotGray) {        // new style graying failed    
  685.         PenPat( (ConstPatternParam) "\xAA\x55\xAA\x55\xAA\x55\xAA\x55");
  686.         if(inColor && (*pd)->osVers >= 0x0700)
  687.             PenMode(notSrcBic);    
  688.         else
  689.             PenMode(patBic);                        
  690.  
  691. //----------------------------------------------------------------------------------
  692. // if entire control is disabled, make frame gray, otherwise, just the item string
  693. //----------------------------------------------------------------------------------
  694.  
  695.         if(inactive) {                                // expand rect to include
  696.             InsetRect(&tempRect,-1,-1);                // frame & drop shadow
  697.             tempRect.bottom+=1;
  698.             tempRect.right+=1;
  699.         }    
  700.         else
  701.         if(itemDisable) {
  702.             tempRect.right-= (*pd)->symWid;            // don't gray the symbol
  703.         }
  704.         
  705.         PaintRect(&tempRect);                        // now it is gray
  706.         
  707.         if(inColor)
  708.             restoreColors(&saveFore,&saveBack);
  709.             
  710. //----------------------------------------------------------------------------------
  711. // if entire control is disabled, make title gray
  712. //----------------------------------------------------------------------------------
  713.  
  714.         if(inactive)
  715.             PaintRect(&(*pd)->rTitle);    
  716.     }
  717.     
  718. //----------------------------------------------------------------------------------
  719. // all done.  Restore colors and font.
  720. //----------------------------------------------------------------------------------
  721.     if(inColor)    
  722.         restoreColors(&saveFore, &saveBack);
  723.     SetPenState(&savePen);
  724.     TextFace(saveFace);
  725.     restoreFont(thisPort, saveFont, saveSize, varCode);
  726. }
  727.  
  728. //==================================================================================
  729. // Pop the menu and invert the title of the control
  730. //==================================================================================
  731.  
  732. static void doPopUp(ControlHandle hCtl, short varCode)
  733. {
  734.     short                saveFont,saveSize,saveFace,pixDepth,newItem=0;
  735.     Point                popPt;
  736.     Style                itemStyle;
  737.     GrafPtr                thisPort;
  738.     RGBColor            saveFore,saveBack,menuFore,menuBack;
  739.     popUpPrivateDataH    pd;
  740.         
  741.     pd = (popUpPrivateDataH)(*hCtl)->contrlData;
  742.     if(!pd)                                                // oops !                    
  743.         return;    
  744.     
  745.     if(!(*pd)->mHandle)                                    // oops again !                
  746.         return;
  747.             
  748.     pixDepth = getPixDepth(&(*hCtl)->contrlRect);        // in color?
  749.     
  750. //----------------------------------------------------------------------------------
  751. // add menu to menulist
  752. //----------------------------------------------------------------------------------
  753.     
  754.     InsertMenu((*pd)->mHandle,-1);
  755.  
  756. //----------------------------------------------------------------------------------
  757. //    Note:  To "fool" the Menu Manager into drawing with the Window Font & size
  758. //    instead of the System Font & size, the setFont() routine sets the global
  759. //    LastSpExtra to -1 to force a rebuild of the font cache.
  760. //  
  761. //    This MUST be done after calling InsertMenu and before calling DeleteMenu, 
  762. //    or we will have a VERY funny looking menubar in the calling application.
  763. //----------------------------------------------------------------------------------
  764.  
  765.     GetPort(&thisPort);
  766.     setFont(thisPort, &saveFont, &saveSize, varCode, (*pd)->hasColorQD);    // do AFTER InsertMenu!
  767.     CalcMenuSize((*pd)->mHandle);    
  768.     
  769. //----------------------------------------------------------------------------------
  770. // Make sure we patch the menu to the correct width.
  771. // See note above about gMenuW global 
  772. //----------------------------------------------------------------------------------
  773.  
  774.     (*pd)->rItem.right-=SHADOW;                            // a little "fixup"
  775.     if((*pd)->symWid == 0)                                // leave width alone
  776.         gMenuW = (*(*pd)->mHandle)->menuWidth;
  777.     else                                                // expand to include symbol
  778.         resizePopup(hCtl, varCode);                        // adjust gMenuW & rItem
  779.         
  780.     if(gMenuW < (**(*pd)->mHandle).menuWidth)            // never size the menu    
  781.         gMenuW = (**(*pd)->mHandle).menuWidth;            // in our patch to a width
  782.                                                         // less than real width.
  783.  
  784.     (**(*pd)->mHandle).menuWidth = gMenuW;                // This needed for System 6
  785.  
  786. //----------------------------------------------------------------------------------
  787. // set item mark before we 'pop' menu menu
  788. //----------------------------------------------------------------------------------
  789.         
  790.     if(!((*pd)->varCode2 & popupNoMark)) {
  791.         if((varCode & popupUseWFont) && (thisPort->txFont != systemFont))
  792.             SetItemMark((*pd)->mHandle,(*hCtl)->contrlValue, DOT);
  793.         else
  794.             SetItemMark((*pd)->mHandle,(*hCtl)->contrlValue, CHECKMARK);
  795.     }
  796.         
  797. //----------------------------------------------------------------------------------
  798. // draw title, if requested, but invert the colors…    
  799. //----------------------------------------------------------------------------------
  800.     
  801.     if((*pd)->varCode2 & popupInsetFrame &&    pixDepth > 2)
  802.         EraseRect(&(*hCtl)->contrlRect);                // remove top/left shadow    
  803.         
  804.     if((*pd)->tWid > 0)    {                                // is there a title?
  805.         if (pixDepth > 2) {                                // in color ?    
  806.             getMenuColors((*pd)->mId, (*hCtl)->contrlValue,
  807.                             &menuFore, &menuBack);        // draw an inverted title    
  808.             saveColors(&saveFore,&saveBack);
  809.             RGBForeColor(&menuBack);                    // N.B ! these are reversed    
  810.             RGBBackColor(&menuFore);
  811.         
  812.             saveFace = thisPort->txFace;    
  813.             itemStyle = (*pd)->tJust >> 8;
  814.             TextFace(itemStyle);
  815.             
  816.             PenPat( (ConstPatternParam) "\x00\x00\x00\x00\x00\x00\x00\x00");
  817.             PaintRect(&(*pd)->rTitle);                    // erase title rect    
  818.             PenPat( (ConstPatternParam) "\xff\xff\xff\xff\xff\xff\xff\xff");
  819. #ifdef _REALOBSCUREBUG
  820.             GetItemStyle((*pd)->mHandle, (*hCtl)->contrlValue, &itemStyle);
  821.             if(itemStyle & outline)
  822.                 (*pd)->vDraw+=1;
  823. #endif                
  824.             MoveTo((*pd)->hTitle,(*pd)->vDraw);    
  825.             DrawString((*hCtl)->contrlTitle);
  826.             
  827.             restoreColors(&saveFore,&saveBack);
  828.             TextFace(saveFace);            
  829.         }
  830.         else {
  831.             InvertRect(&(*pd)->rTitle);
  832.         }
  833.     }
  834.     
  835. //----------------------------------------------------------------------------------
  836. // get ready to pop the menu    
  837. //----------------------------------------------------------------------------------
  838.  
  839.     popPt.h = (*pd)->rItem.left+1;                        // position for pop-up        
  840.     popPt.v = (*pd)->rItem.top+1;
  841.     LocalToGlobal(&popPt);                                // convert to global coords    
  842.     
  843.     patchCalcMenuSize(true);                            // install our patch
  844.  
  845. #ifdef THINK_C    
  846.     RememberA4();
  847. #endif
  848.         
  849. //----------------------------------------------------------------------------------
  850. // now we can pop the menu 
  851. //----------------------------------------------------------------------------------
  852.  
  853.     newItem = PopUpMenuSelect((*pd)->mHandle,popPt.v,popPt.h,(*hCtl)->contrlValue);
  854.  
  855. //----------------------------------------------------------------------------------
  856. // now clear item mark, set controlValue, cleanup and exit    
  857. //----------------------------------------------------------------------------------
  858.  
  859.     patchCalcMenuSize(false);                            // remove our patch
  860.     
  861.     SetItemMark((*pd)->mHandle,(*hCtl)->contrlValue, noMark);
  862.     
  863.     if(newItem != 0) {                                    // anything chosen ?
  864.         (*hCtl)->contrlValue = newItem;                    // set new control value
  865.     }
  866.     restoreFont(thisPort, saveFont, saveSize, varCode);    // do BEFORE DeleteMenu()!!
  867.         
  868.     (*hCtl)->contrlMax = CountMItems((*pd)->mHandle);    // new items??
  869.     
  870. //----------------------------------------------------------------------------------
  871. // Remove menu from menu list and restore colors
  872. //----------------------------------------------------------------------------------
  873.     
  874.     if((*(*pd)->mHandle)->menuID > 0 &&
  875.         !((*pd)->varCode2 & popupNoDeleteMenu)) {
  876.         DeleteMenu((*(*pd)->mHandle)->menuID);            // trashes the 'mctb' too
  877.         if (pixDepth > 1)
  878.             setMenuColors((*(*pd)->mHandle)->menuID);    // need 'em in doDraw
  879.     }
  880.     
  881.     EraseRect(&(*pd)->rTitle);                            // clear inverted title    
  882. }
  883.  
  884. //==================================================================================
  885. //    Draw the popup symbol - a downward pointing triangle
  886. //==================================================================================
  887.  
  888. static void drawSymbol(Rect r, short symHt, short symWid)
  889. {
  890.     short                inx,wid,inset;
  891.  
  892.     wid = symHt*2-2;
  893.     inx = (r.right - r.left)-6;
  894.     if(symWid && inx > symWid)                            // center in rect if needed
  895.         inset = 6;                                    
  896.     else
  897.         inset = ((r.right - r.left) - wid)/2+1;            // for compatibility with CDEF 63
  898.     
  899.     MoveTo(r.right - wid - inset, r.top+((r.bottom-r.top) - symHt)/2);
  900.     for(inx=1;inx<=symHt;inx++) {
  901.         Line(wid,0);
  902.         Move(-wid+1,+1);
  903.         wid-=2;
  904.     }
  905. }
  906. //==================================================================================
  907. //    Draw the normal, drop shadowed popup
  908. //==================================================================================
  909.  
  910. static void drawPopupFrame(Rect * r)
  911. {
  912.     FrameRect(r);                            // popUp Menu frame    
  913.     MoveTo((*r).left+SHADOW,(*r).bottom);    // do a drop shadow    
  914.     LineTo((*r).right,(*r).bottom);            // along bottom edge…        
  915.     LineTo((*r).right,(*r).top+SHADOW);        // and along right edge…        
  916. }
  917. //==================================================================================
  918. //    Calculate various Rects used in drawing.
  919. //  This is key to the whole CDEF - we must draw the popup in the same location
  920. //  as the Menu Manager will when we pop the menu.  If we don't line things up
  921. //  exactly, the item text and icon will 'shift' when we pop the menu.
  922. //==================================================================================
  923.  
  924. static void getRects(ControlHandle hCtl, Boolean sysFont)
  925. {
  926.     short                wid,ht,fHt;
  927.     short                vOff,iconNum=0,iconWid,iconHt,cmdChar;
  928.     Style                itemStyle,titleStyle;
  929.     unsigned char        tJust;
  930.     FontInfo            fInfo;
  931.     Rect                rTemp;
  932.     popUpPrivateDataH    pd;
  933.     
  934.     pd = (popUpPrivateDataH)(*hCtl)->contrlData;
  935.     
  936.     if(!pd)                                                // oops !                    
  937.         return;    
  938.  
  939. //----------------------------------------------------------------------------------
  940. // If we will draw more than the symbol, check for icon
  941. //----------------------------------------------------------------------------------
  942.  
  943.     GetItemCmd((*pd)->mHandle, (*hCtl)->contrlValue, &cmdChar);
  944.     if(cmdChar != 0x1C)
  945.         GetItemIcon((*pd)->mHandle, (*hCtl)->contrlValue, &iconNum);
  946.     
  947. //----------------------------------------------------------------------------------
  948. // get font metrics
  949. //----------------------------------------------------------------------------------
  950.  
  951.     GetItemStyle((*pd)->mHandle, (*hCtl)->contrlValue, &itemStyle);
  952.     TextFace(itemStyle);
  953.         
  954.     GetFontInfo(&fInfo);
  955.     ht = fInfo.ascent+fInfo.descent+fInfo.leading;        // height of menu item
  956.     
  957.     TextFace(normal);
  958.     GetFontInfo(&fInfo);
  959.     fHt = fInfo.ascent+fInfo.descent+fInfo.leading;
  960.     if(sysFont)
  961.         fInfo.widMax-=2;                                // System MDEF fudges this
  962.     fInfo.widMax+=3;                                    // and we fudge again…
  963. //----------------------------------------------------------------------------------
  964. // make adjustments for icon items if needed
  965. //----------------------------------------------------------------------------------
  966.     if(iconNum) {
  967.         SetRect(&rTemp, 0, 0, 32, 32);                    // b&w icon always 32x32
  968.         switch(cmdChar) {
  969.             case 0x1D:                                    // reduced icon, 'ICON' or 'cicn'
  970.             case 0x1E:                                    // small icon 'SICN'        
  971.                 iconWid = iconHt = 16;
  972.             break;
  973.             case 0:
  974.                 getIconSize(iconNum, &rTemp);
  975.                 iconWid = rTemp.right - rTemp.left;
  976.                 iconHt  = rTemp.bottom- rTemp.top;
  977.             break;
  978.         }
  979.         if(iconHt + 2 >= ht)
  980.             ht = iconHt + 2;
  981.     }
  982.     
  983. //----------------------------------------------------------------------------------
  984. // calculate title rect
  985. //----------------------------------------------------------------------------------
  986.  
  987.     (*pd)->rTitle = (*hCtl)->contrlRect;                    // title rect                    
  988.     (*pd)->rTitle.right = (*pd)->rTitle.left + (*pd)->tWid;    // width for title string    
  989.     (*pd)->rTitle.top = (*pd)->rTitle.bottom - ht;            // height matches menu hilite
  990.  
  991.     OffsetRect(&(*pd)->rTitle, 0, -2);                        // adjust for drop shadow
  992.  
  993. //----------------------------------------------------------------------------------
  994. // calcuate item rect
  995. //----------------------------------------------------------------------------------
  996.  
  997.     (*pd)->rItem = (*hCtl)->contrlRect;                        // popup rect
  998.     
  999.     if((*pd)->rItem.top < (*pd)->rItem.bottom - (ht+SHADOW))
  1000.         (*pd)->rItem.top = (*pd)->rItem.bottom - (ht+SHADOW);
  1001.         
  1002.     (*pd)->rItem.left = (*pd)->rTitle.right;
  1003.  
  1004. //----------------------------------------------------------------------------------
  1005. // Center the item & title rects in the control rect and calcuate text draw points
  1006. //----------------------------------------------------------------------------------
  1007.  
  1008.     vOff = (((*hCtl)->contrlRect.bottom-(*hCtl)->contrlRect.top) -
  1009.             ((*pd)->rItem.bottom-(*pd)->rItem.top))/2;
  1010.             
  1011.     OffsetRect(&(*pd)->rItem, 0, -vOff);
  1012.     OffsetRect(&(*pd)->rTitle, 0, -vOff);
  1013.     
  1014.     (*pd)->vDraw = (*pd)->rItem.top+fInfo.ascent + 1;        // save draw points
  1015.     (*pd)->hItem = (*pd)->rItem.left+fInfo.widMax;
  1016.  
  1017. //----------------------------------------------------------------------------------
  1018. // calculate icon rect & item draw point
  1019. //----------------------------------------------------------------------------------
  1020.  
  1021.     if(iconNum) {
  1022.         (*pd)->rIcon = (*pd)->rTitle;
  1023.         (*pd)->rIcon.bottom = (*pd)->rIcon.top + iconHt;
  1024.         vOff = (ht+1-iconHt)/2;                                // center icon in
  1025.         OffsetRect(&(*pd)->rIcon, 0, vOff);                    // item rect
  1026.         
  1027.          if((*pd)->varCode2 & popupIconOnly)
  1028.             (*pd)->rIcon.left = (*pd)->rTitle.right + 2;    // skip item text inset
  1029.         else
  1030.             (*pd)->rIcon.left = (*pd)->rTitle.right + fInfo.widMax;
  1031.             
  1032.         (*pd)->rIcon.right = (*pd)->rIcon.left + iconWid;
  1033.  
  1034. //----------------------------------------------------------------------------------
  1035. // now, adjust the draw point for the item text string
  1036. //----------------------------------------------------------------------------------
  1037.  
  1038.         if(iconHt >= fHt)
  1039.             (*pd)->vDraw = (*pd)->vDraw + (iconHt - fHt)/2 + 1;
  1040.             
  1041.         (*pd)->hItem = (*pd)->hItem + iconWid + CharWidth(0x20);
  1042.     }
  1043.     
  1044. //----------------------------------------------------------------------------------
  1045. // calculate size of popup symbol - menu will be wider by this amount
  1046. //----------------------------------------------------------------------------------
  1047.  
  1048.     if(((*pd)->varCode2 & popupNoSymbol) ||                    // don't need extra space.
  1049.             ((*pd)->varCode2 & popupIconOnly) ||
  1050.                 ((*pd)->varCode2 & popupCenterText))    
  1051.         (*pd)->symWid = 0;
  1052.     else {
  1053.         (*pd)->symHt = fHt/3 + 1;                            // height of symbol
  1054.         (*pd)->symWid = (*pd)->symHt * 2 + fHt/3;            // this is exact for Chi 12
  1055.                                                             // and close for all others.
  1056.                                                             
  1057.         if((*pd)->varCode2 & popupSymbolOnly)    {            // Adjust rect for symbol
  1058.             if((*pd)->tWid == 0)
  1059.                 (*pd)->rItem.left+=1;
  1060.             (*pd)->rItem.right = (*pd)->rItem.left +         // only menus.
  1061.                                     (*pd)->symHt*2 + 12;    // 6 pixel "border"
  1062.         }
  1063.         else {                                                // compatible with CDEF 63
  1064.             wid = (*hCtl)->contrlRect.right -
  1065.                     (*hCtl)->contrlRect.left-1;                // controls that are very
  1066.             if(wid <= (*pd)->symWid) {                        // narrow.
  1067.                 (*pd)->symWid = 0;                            // don't expand width.
  1068.             }
  1069.         }
  1070.     }
  1071.     
  1072. //----------------------------------------------------------------------------------
  1073. // calculate title string justification    
  1074. //----------------------------------------------------------------------------------
  1075.     
  1076.     if((*pd)->tWid) {                                    // we have a title string
  1077.         tJust = (*pd)->tJust & 0xff;                    // get title justification
  1078.         
  1079.         titleStyle = (*pd)->tJust >> 8;                    // get title style
  1080.         TextFace(titleStyle);
  1081.         
  1082.         if((*pd)->tWid > 0) {                            // horiz coord from tJust
  1083.             wid = StringWidth((*hCtl)->contrlTitle);
  1084.             if (tJust == popupTitleRightJust)
  1085.                 (*pd)->hTitle = (*pd)->rTitle.right - wid - 3;
  1086.             else
  1087.             if (tJust == popupTitleCenterJust)            
  1088.                 (*pd)->hTitle = (*pd)->rTitle.left + 
  1089.                     (((*pd)->rTitle.right - (*pd)->rTitle.left) - wid)/2;
  1090.             else
  1091.                 (*pd)->hTitle = (*pd)->rTitle.left+4;    // left justified (+4)
  1092.         }
  1093.     }
  1094.  
  1095. //----------------------------------------------------------------------------------
  1096. // System 6 MDEF draws a little differently with an ICON
  1097. //----------------------------------------------------------------------------------
  1098.     
  1099.     if((*pd)->osVers < 0x0700 && iconNum > 0) {
  1100.         OffsetRect(&(*pd)->rIcon, 0, 1);
  1101.         (*pd)->rItem.bottom++;
  1102.         (*pd)->rTitle.bottom+=2;
  1103.         (*pd)->vDraw++;
  1104.     }
  1105.  
  1106. }
  1107.  
  1108. //==================================================================================
  1109. //    Resize the menu and the controlRect if needed.  This is called from our
  1110. //    doDraw routine and from the doPopUp routine.  The doPopUp routine will
  1111. //    insure that gMenuW is at least as wide as the normal menu.
  1112. //
  1113. //    Rules are:
  1114. //        1. gMenuW cannot be wider than the control.
  1115. //        2. If popupFixedWidth, gMenuW must equal control width.
  1116. //        2. If not popupFixedWidth, control rect must shrink to fit menu width.
  1117. //==================================================================================
  1118.  
  1119. static void    resizePopup(ControlHandle hCtl, short varCode)
  1120. {
  1121.     short                cntlW;
  1122.     popUpPrivateDataH    pd;
  1123.     
  1124.     pd = (popUpPrivateDataH)(*hCtl)->contrlData;
  1125.     
  1126.     cntlW = (*pd)->rItem.right - (*pd)->rItem.left;            // width of control
  1127.     
  1128.     gMenuW = (**(*pd)->mHandle).menuWidth +                 // width of menu plus
  1129.                 (*pd)->symWid;                                // symbol width
  1130.             
  1131.     if(gMenuW > cntlW)                                        // limit menu to the
  1132.         gMenuW = cntlW;                                        // control width
  1133.     else {
  1134.         if(varCode & popupFixedWidth)                        // expand menu to the
  1135.             gMenuW = cntlW;                                    // control width
  1136.         else                                                // shrink item rect
  1137.             (*pd)->rItem.right = (*pd)->rItem.left + gMenuW;
  1138.     }
  1139. }
  1140.  
  1141. //==================================================================================
  1142. //    widen the menu to allow for the "popUp" symbol width
  1143. //==================================================================================
  1144.  
  1145. #ifdef __powerc
  1146. enum {
  1147.     uppCalcMenuSizeInfo= kPascalStackBased
  1148.         | RESULT_SIZE(SIZE_CODE(sizeof(0)))
  1149.         | STACK_ROUTINE_PARAMETER(1, SIZE_CODE(sizeof(Handle)))
  1150. };
  1151. #endif
  1152.  
  1153. static pascal void myCalcMenuSize(MenuHandle hMenu)
  1154. {
  1155.  
  1156. #ifdef __powerc
  1157.  
  1158.     CallUniversalProc(CalcMenuSizeUPP, uppCalcMenuSizeInfo, hMenu);
  1159.     (**hMenu).menuWidth = gMenuW;        // Replace the menuWidth with new value    
  1160.             
  1161. #endif
  1162.  
  1163. #ifdef THINK_C    
  1164.  
  1165.     SetUpA4();                                            // Restore A4 world so we can use our globals
  1166.     CallCalcMenuSize( hMenu );            // Call the real CalcMenuSize
  1167.     (**hMenu).menuWidth = gMenuW;        // Replace the menuWidth with new value            
  1168.     RestoreA4();
  1169.  
  1170. #endif
  1171.  
  1172. #ifndef __powerc
  1173. #ifdef    __MWERKS__
  1174.     long    oldA4;
  1175.  
  1176.     oldA4 = SetCurrentA4();
  1177.     CallCalcMenuSize(hMenu);
  1178.     (**hMenu).menuWidth = gMenuW;        // Replace the menuWidth with new value
  1179.     SetA4(oldA4);
  1180.     
  1181. #endif
  1182. #endif
  1183. }
  1184.  
  1185. //==================================================================================
  1186. //    Patch the CalcMenus trap so we can adjust the width of the menu
  1187. //==================================================================================
  1188.  
  1189. static void patchCalcMenuSize(short patch)
  1190. {
  1191.  
  1192. #ifdef __powerc
  1193.     THz                                 saveZone;
  1194.     UniversalProcPtr    myCalcMenuSizeUPP;
  1195.     
  1196.         saveZone = GetZone();
  1197.         SetZone(SystemZone());
  1198.         
  1199.         if(patch) {
  1200.             CalcMenuSizeUPP = NGetTrapAddress(_CalcMenuSize,ToolTrap);
  1201.             myCalcMenuSizeUPP = NewRoutineDescriptor (
  1202.                                         (ProcPtr) myCalcMenuSize,
  1203.                                         uppCalcMenuSizeInfo,
  1204.                                         GetCurrentISA());
  1205.             NSetTrapAddress((UniversalProcPtr)myCalcMenuSizeUPP,_CalcMenuSize,ToolTrap);
  1206.             
  1207.         }
  1208.         else {
  1209.             NSetTrapAddress(CalcMenuSizeUPP,_CalcMenuSize,ToolTrap);
  1210.         }
  1211.         
  1212.         SetZone(saveZone);
  1213. #else
  1214.         if(patch) {
  1215.             CalcMenuSizeUPP = NGetTrapAddress(_CalcMenuSize,ToolTrap);    
  1216.             NSetTrapAddress((UniversalProcPtr)myCalcMenuSize,_CalcMenuSize,ToolTrap);
  1217.         }
  1218.         else {    
  1219.             NSetTrapAddress(CalcMenuSizeUPP,_CalcMenuSize,ToolTrap);
  1220.         }
  1221. #endif
  1222. }
  1223.  
  1224. //==================================================================================
  1225. // get the colors from MCEntry table for drawing the popUp menu
  1226. //==================================================================================
  1227.  
  1228. static void getMenuColors(short menuID, short menuItem, 
  1229.                             RGBColor *menuFore, RGBColor *menuBack)
  1230. {
  1231.     MCEntryPtr    mcPtr;
  1232.     
  1233.     menuBack->red = -1;                                // default to black on white    
  1234.     menuBack->green = -1;
  1235.     menuBack->blue = -1;
  1236.     menuFore->red = 0;
  1237.     menuFore->green = 0;
  1238.     menuFore->blue = 0;
  1239.     
  1240.     mcPtr = GetMCEntry(menuID,menuItem);            // does this item have a color?
  1241.     if(mcPtr != 0L) {
  1242.         BlockMove(&mcPtr->mctRGB4,menuBack,sizeof(RGBColor));
  1243.         BlockMove(&mcPtr->mctRGB2,menuFore,sizeof(RGBColor));
  1244.     }
  1245.     else {
  1246.         mcPtr = GetMCEntry(menuID,0);                // nope, try this menu
  1247.         if(mcPtr != 0L) {
  1248.             BlockMove(&mcPtr->mctRGB4,menuBack,sizeof(RGBColor));
  1249.             BlockMove(&mcPtr->mctRGB3,menuFore,sizeof(RGBColor));
  1250.         }
  1251.         else {
  1252.             mcPtr = GetMCEntry(0,0);                // nope, try the menubar
  1253.             if(mcPtr != 0L) {
  1254.                 BlockMove(&mcPtr->mctRGB2,menuBack,sizeof(RGBColor));
  1255.                 BlockMove(&mcPtr->mctRGB3,menuFore,sizeof(RGBColor));
  1256.             }
  1257.         }
  1258.     }
  1259. }
  1260.  
  1261. //==================================================================================
  1262. // restore the MCEntries for menu - call to Delete menu wiped 'em out
  1263. //==================================================================================
  1264. static void setMenuColors(short menuID)
  1265. {
  1266.     short    cnt;
  1267.     Handle    hMctb;
  1268.     
  1269.     hMctb=Get1Resource('mctb', menuID);
  1270.     if(hMctb) {
  1271.         cnt=(GetHandleSize(hMctb)-sizeof(short))/sizeof(MCEntry);
  1272.         HLock(hMctb);
  1273.         SetMCEntries(cnt, (MCTablePtr)((*(char **)hMctb)+sizeof(short)));
  1274.         HUnlock(hMctb);
  1275.         ReleaseResource(hMctb);
  1276.     }
  1277. }
  1278.  
  1279. //==================================================================================
  1280. //  Set font for control drawing & force rebuild of font cache
  1281. //==================================================================================
  1282.  
  1283. static Boolean setFont(GrafPtr thisPort, short *saveFont, short *saveSize, 
  1284.                         short varCode, Boolean hasColorQD)
  1285. {
  1286.     Boolean     sysFont = false;
  1287.     GrafPtr        currPort, wMgrPort;
  1288.     CGrafPtr    wMgrCPort;
  1289.     
  1290.     if(varCode & popupUseWFont) {                    // use current port's font & size
  1291.  
  1292. //----------------------------------------------------------------------------------
  1293. //    Set the textSize and textFont of current window manager port to defaults.
  1294. //    This is a work-around against some misbehaved apps from MicroSoft and Claris.
  1295. //    Thanks to Rick Christianson for this fix.
  1296. //----------------------------------------------------------------------------------
  1297.      
  1298.         GetPort(&currPort);
  1299.         
  1300.         if (hasColorQD) {
  1301.             GetCWMgrPort(&wMgrCPort);
  1302.             SetPort((GrafPtr)wMgrCPort);
  1303.         }
  1304.         else {
  1305.             GetWMgrPort(&wMgrPort);
  1306.             SetPort(wMgrPort);
  1307.         }
  1308.         TextSize(0);
  1309.         TextFont(0);    
  1310.              
  1311.         SetPort(currPort);
  1312.      
  1313.         if(thisPort->txFont == systemFont)
  1314.             sysFont = true;    
  1315.         *saveFont = LMGetSysFontFam();                // save current system font number        
  1316.         *saveSize = LMGetSysFontSize();                // save current system font size         
  1317.         LMSetSysFontFam(thisPort->txFont);            // set to the current port settings    
  1318.         LMSetSysFontSize(thisPort->txSize);
  1319.         LMSetLastSPExtra(-1L);                        // force rebuild of font cache
  1320.     }
  1321.     else {                                            // use system font & size    
  1322.         sysFont = true;            
  1323.         *saveFont = thisPort->txFont;                // save current font & size                
  1324.         *saveSize = thisPort->txSize;
  1325.         thisPort->txFont = LMGetSysFontFam();
  1326.         thisPort->txSize = LMGetSysFontSize();
  1327.     }
  1328.     return(sysFont);                                // getRects wants to know…
  1329. }
  1330.  
  1331. //==================================================================================
  1332. //  Restore font & force rebuild of font cache
  1333. //==================================================================================
  1334.  
  1335. static void restoreFont(GrafPtr thisPort, short saveFont, short saveSize, short varCode)
  1336. {
  1337.     if(varCode & popupUseWFont)    {                    // restore system font & size info        
  1338.         LMSetSysFontFam(saveFont);                    
  1339.         LMSetSysFontSize(saveSize);
  1340.         LMSetLastSPExtra(-1L);                        // force rebuild of font cache            
  1341.     }
  1342.     else {                                            // restore current port settings        
  1343.         thisPort->txFont = saveFont;
  1344.         thisPort->txSize = saveSize;
  1345.     }
  1346. }
  1347.  
  1348. //==================================================================================
  1349. //  return the size of the icon for a menu item, regardless of type.
  1350. //    returns TRUE if icon is a 'cicn'.
  1351. //==================================================================================
  1352. static void getIconSize(short iconNum, Rect *rIcon)
  1353. {
  1354.     Handle    hIcon=0;
  1355.     
  1356.     iconNum+=256;
  1357.                                         
  1358.     hIcon = GetResource('cicn', iconNum);                // try for a color icon
  1359.     if(hIcon) {
  1360.         *rIcon = (**(PixMapHandle)hIcon).bounds;        // could be any size…
  1361.         ReleaseResource(hIcon);
  1362.     }
  1363. }
  1364. //==================================================================================
  1365. //  Plot an icon of any type or size
  1366. //==================================================================================
  1367. static void plotAnIcon(short iconNum, Rect *rIcon, short osVers, short cmdChar,
  1368.                         Boolean inactive, Boolean hasColorQD, Boolean inColor) 
  1369. {
  1370.     char        state;
  1371.     Boolean        isSICN=false;
  1372.     BitMap        iBits;
  1373.     Handle        hIcon=0;
  1374.     CIconHandle    hCIcon;
  1375.     GrafPtr        thisPort;
  1376.     
  1377. //----------------------------------------------------------------------------------
  1378. // Initialize
  1379. //----------------------------------------------------------------------------------
  1380.     iconNum += 256;                                    // MenuMgr weirdness…
  1381.     if(cmdChar == 0x1E)
  1382.         isSICN = true;
  1383.     
  1384.     if(isSICN) {
  1385.         hIcon = GetResource('SICN', iconNum);
  1386.     }
  1387.     else {
  1388.         if(hasColorQD) {
  1389.             hCIcon = GetCIcon(iconNum);                // try for color icon first
  1390.             if(hCIcon) {                            // got it!
  1391.                 if(inColor && osVers >= 0x0700)        // use new routine
  1392.                     PlotCIconHandle(rIcon, 0, (short)inactive, hCIcon);
  1393.                 else
  1394.                     PlotCIcon(rIcon, hCIcon);        // original routine
  1395.                     
  1396.                 DisposCIcon(hCIcon);
  1397.                 return;                                // all done…
  1398.             }
  1399.         }
  1400.         hIcon = GetResource('ICON', iconNum);        // fall back to B&W icon
  1401.     }
  1402.     
  1403. //----------------------------------------------------------------------------------
  1404. // if we get to here, we are plotting an ICON, reduced ICON or SICN.
  1405. //----------------------------------------------------------------------------------
  1406.     if(hIcon) {
  1407.         GetPort(&thisPort);                            // draw in correct port
  1408.         state = HGetState(hIcon);
  1409.         HLock(hIcon);
  1410.         
  1411. //----------------------------------------------------------------------------------
  1412. // Use the nifty new Icon utilities if we can.  This has a nice advantage under
  1413. // System 7 - the icon data is centered in the destination rect.
  1414. //----------------------------------------------------------------------------------
  1415.         if(osVers >= 0x0700) {
  1416.             if(isSICN)
  1417.                 PlotSICNHandle(rIcon, atAbsoluteCenter, ttNone, hIcon);
  1418.             else
  1419.                 PlotIconHandle(rIcon, atAbsoluteCenter, ttNone, hIcon);
  1420.         }
  1421.         else {
  1422. //----------------------------------------------------------------------------------
  1423. // Well, since we don't have the new Icon utilities, do it by hand with CopyBits.
  1424. // We won't get the nice centering of SICN and ICON data.  A nice tight routine
  1425. // is needed…
  1426. //
  1427. // Set up our bitmap for the correct icon size - 
  1428. //----------------------------------------------------------------------------------
  1429.  
  1430.             iBits.baseAddr = *hIcon;
  1431.             if(isSICN) {
  1432.                 iBits.rowBytes = 2;
  1433.                 SetRect(&iBits.bounds, 0, 0, 16, 16);
  1434.             }
  1435.             else {
  1436.                 iBits.rowBytes = 4;
  1437.                 SetRect(&iBits.bounds, 0, 0, 32, 32);
  1438.             }
  1439.                     
  1440. //----------------------------------------------------------------------------------
  1441. // Now, blit it onscreen.
  1442. //----------------------------------------------------------------------------------
  1443.  
  1444.             CopyBits(    &iBits,
  1445.                         &thisPort->portBits,
  1446.                         &iBits.bounds,
  1447.                          rIcon,
  1448.                          srcCopy, nil);
  1449.         }
  1450.         HSetState(hIcon, state);
  1451.         ReleaseResource(hIcon);
  1452.     }
  1453. }
  1454.  
  1455. //==================================================================================
  1456. //  Check for ColorQuickDraw - see qdCDEF.c if you need the version of ColorQD
  1457. //==================================================================================
  1458. Boolean hasColorQD()        
  1459. {
  1460.     OSErr        err;
  1461.     long        gResult;
  1462.     
  1463.     if(trapAvailable(_Gestalt)) {        
  1464.         err = Gestalt(gestaltQuickdrawVersion,&gResult);
  1465.         if(err == noErr)
  1466.             if(LoWord(gResult) >= gestalt8BitQD)
  1467.                 return(true);
  1468.     }
  1469.     return(false);
  1470. }